【Java学习日记】Mybatis

您所在的位置:网站首页 mybatis deletebatchids 【Java学习日记】Mybatis

【Java学习日记】Mybatis

2023-07-20 05:38| 来源: 网络整理| 查看: 265

目录 Mybatis-Plus笔记1. 快速入门1. 创建数据库2. 编写、初始化项目!使用SpringBoot初始化!3. 连接数据库4. 配置项目 2. 配置日志CRUD扩展插入操作主键生成策略雪花算法 更新操作自动填充乐观锁查询操作查询多条条件查询(无模糊) 分页查询删除操作通过id删除单条通过id批量删除条件删除 逻辑删除 3. 性能分析插件1、导入插件2、测试使用 条件构造器代码自动生成器(旧)代码自动生成器(新)

Mybatis-Plus笔记

MyBatis-Plus官方网址

​ MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

​ 很多开发也会选择使用MP来开发,本文是作者根据B站up主“遇见狂神说”所出的MyBtis-Plus教程学习得来的笔记,主要应用MP 3.0.5版本进行开发,同时也对一些新版特性有所总结,并且分享出来希望可以共同进步。

1. 快速入门 1. 创建数据库 DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` BIGINT(20) NOT NULL COMMENT '主键ID', `name` VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', `age` INT(11) NULL DEFAULT NULL COMMENT '年龄', `email` VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8; INSERT INTO `user`(`id`,`name`,`age`,`email`) VALUES (1,'Jone',18,'[email protected]'), (2,'Jack',20,'[email protected]'), (3,'Tom',28,'[email protected]'), (4,'Sandy',21,'[email protected]'), (5,'Billie',24,'[email protected]'); -- 真实开发中,version(乐观锁)、deleted(逻辑制除)、gmt_create,gmt_modified 2. 编写、初始化项目!使用SpringBoot初始化! mysql mysql-connector-java 5.1.25 runtime org.projectlombok lombok com.baomidou mybatis-plus-boot-starter 3.0.5 com.h2database h2 runtime

注意:MyBatis和MyBatis-Plus不要同时导入,会产生版本差异!

3. 连接数据库 #mysql 5 驱动不同com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.username=root spring.datasource.password=root #mysql 8 驱动不同com.mysql.cj.jdbc.Driver、需要增加时区配置serverTimezone=GMT%2B8 spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&userUnicode=true&charEncoding=utf-8&serverTimezone=GMT%2B8 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.username=root spring.datasource.password=root 4. 配置项目

传统方式:pojo(entity)-mapper(dao)(连接MyBatis,配置mapper)-service-controller

使用MyBatis-Plus之后:

entity/pojo

​ 创建实体类:

/** * @Data:@Data 注解可以应用在类上,它会自动生成以下方法: * 所有字段的 getter 和 setter 方法 * equals() 和 hashCode() 方法 * toString() 方法 * 自动为非静态字段生成无参构造方法 * @AllArgsConstructor:@AllArgsConstructor 注解可以应用在类上,它会为类的所有字段生成一个包含所有参数的构造方法。 * @NoArgsConstructor:@NoArgsConstructor 注解可以应用在类上,它会为类生成一个无参构造方法。 */ @Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String name; private Integer age; private String email; } mapper接口

​ 创建mapper接口

/** * 在对应的Mapper上面继承基本的类BaseMapper * 注解可用@Repository也可以表示持久层 */ @Mapper public interface UserMapper extends BaseMapper { //所有的CRUD操作已经编写完成 } 使用(注意点:我们需要在主启动类上去扫描mapper包下的所有接口@MapperScan(value = "com.team.mp.mapper"))

配置启动类

/** * mapper配置了@Mapper可以不写@MapperScan * @MapperScan(value = "com.team.mp.mapper") */ @SpringBootApplication public class MybatisPlusApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusApplication.class, args); } } 测试

测试代码

@SpringBootTest class MybatisPlusApplicationTests { @Autowired private UserMapper userMapper; @Test void contextLoads() { /** * 参数是一个Wrapper,条件构造器,这里先不用,填null * 查询全部用户 */ List users = userMapper.selectList(null); users.forEach(System.out::println); } }

运行结果

在 IntelliJ IDEA 中连接数据库,可以使用以下插件来简化和增强数据库开发的功能:(其实是方便看数据库的表和字段)

Database Tools and SQL 插件(内置):IntelliJ IDEA 已经内置了一个 Database 工具和 SQL 插件,可以通过此插件连接和管理数据库。您可以在 IDEA 的 “View” 菜单下选择 “Tool Windows” -> “Database”,然后点击 “+” 按钮添加数据库连接。该插件支持多种数据库,包括 MySQL、Oracle、PostgreSQL 等,并提供了基本的数据库操作、SQL 编辑和查询功能。DBeaver 插件:DBeaver 是一个功能强大的开源数据库客户端工具,也提供了 IntelliJ IDEA 的插件版本。通过安装 DBeaver 插件,您可以在 IDEA 中直接使用 DBeaver 的功能,包括连接和管理多种数据库、执行 SQL 查询、编辑数据库对象等。您可以在 IntelliJ IDEA 的插件市场中搜索 “DBeaver” 并安装该插件。Navicat 插件:Navicat 是一款流行的商业数据库管理工具,其也提供了 IntelliJ IDEA 的插件版本。通过安装 Navicat 插件,您可以在 IDEA 中使用 Navicat 的功能,包括连接和管理多种数据库、执行 SQL 查询、导入导出数据等。您可以在 IntelliJ IDEA 的插件市场中搜索 “Navicat” 并安装该插件。

这些插件都可以在 IntelliJ IDEA 的插件市场中进行搜索和安装。根据您对数据库工具的偏好和项目需求,选择适合的插件来进行数据库开发。

2. 配置日志

所有的sql目前是不可见的,希望知道执行的过程

#配置日志(数据库连接配置) mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl CRUD扩展 插入操作

insert扩展

//测试插入 @Test public void testInsert(){ User user = new User(); user.setName("lyy"); user.setAge(3); user.setEmail("[email protected]"); int result = this.userMapper.insert(user);//帮我们自动生成id System.out.println(result);//受影响行数 System.out.println(user);//发现id会自动回填 }

image-20230714185215671

数据库插入的id默认值为:全局的唯一id

主键生成策略

默认 ID_WORKER(过时)现在新版本是ASSIGN_ID(雪花算法)全局唯一id

对应数据库中的主键(uuid、自增id、雪花算法、redis、zookeeper!)

分布式系统唯一Id生成:https://www.cnblogs.com/haoxinyue/p/5208136.html

雪花算法

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心(数据服务器中心,例如北京、上海、西雅图等等),5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯一!

主键自增

1、实体类字段上@TableId(type = IdType.AUTO)

2、数据库主键一定要勾选自增!

3、再次测试插入即可!

其余的源码解释(alt+点击注解中的IdTypej进入)

public enum IdType { AUTO(0),//数据库id自增 NONE(1),//未设置主键 INPUT(2),//手动输入 ID_WORKER(3),//默认的全局唯一id UUID(4),//全局唯一id uuid ID_WORKER_STR(5);//ID_WORKER 字符串表示法 } 更新操作 //测试更新 @Test public void testUpdate(){ User user = new User(); //通过条件拼接动态sql user.setId(6L); user.setName("lyy3"); user.setAge(18); //注意:updateById 但是参数是一个 对象! int result = this.userMapper.updateById(user); System.out.println(result); }

所有sql都是自动动态配置

自动填充

创建时间、修改时间,这些操作一般都是自动化完成的,一般不希望手动更新!

阿里巴巴开发手册︰几乎所有的表都要配置 gmt_create、gmt_modified !而且需要自动化!

方式一:数据库级别

1、在表中新增字段create_time, update_time

在这里插入图片描述

2、再次测试插入方法,先同步实体类

private Date createTime; private Date updateTime;

3、再次更新查看结果即可

https://gitee.com/lin-yuyang/image-storage/raw/master/https://gitee.com/lin-yuyang/image-storage/image-20230714185331609.png

方式二:代码级别

1、删除数据库的默认值、更新操作

2、实体类的字段属性上增加注解

//字段填充内容 @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;

3、编写处理器处理上述注解

@Slf4j //一定不要忘记把处理器加入到IOC容器中,一定要加@Component @Component public class MyMetaObjectHandler implements MetaObjectHandler { /** * 插入时的填充策略 * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { log.info("Start insert fill......"); /** * @param fieldName 字段名 * @param fieldVal 想要插入的字段字 * @param metaObject 要给哪个数据处理 */ this.setFieldValByName("createTime", new Date(), metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); } /** * 更新时的填充策略 * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { log.info("Start update fill......"); this.setFieldValByName("updateTime", new Date(), metaObject); } }

新版实现类

@Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill ...."); this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用) // 或者 this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐) // 或者 this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug) } @Override public void updateFill(MetaObject metaObject) { log.info("start update fill ...."); this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐) // 或者 this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐) // 或者 this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug) } }

4、测试插入、更新,观察时间即可!

乐观锁

乐观锁:顾名思义十分乐观,总会假定认为不会出现问题,干什么都不会上锁。如果出了问题,再次更新值测试

悲观锁:顾名思义十分悲观,总会假定认为总会出现问题,干什么都会先上锁。再去操作

乐观锁实现方式:(当更新一条记录的时候,希望这条记录没有被别人更新)

取出记录时,获取当前 version更新时,带上这个 version执行更新时, set version = newVersion where version = oldVersion如果 version 不对,就更新失败 -- 乐观锁:1.先查询,获得版本号 version == 1,每次执行version -- A线程 update user set name = 'lin', version = version + 1 where id = 2 and version = 1 -- B线程抢先完成,这个时候version == 2,此时A执行完版本号应该为1,不为2,所以会导致A线程执行失败 update user set name = 'lin', version = version + 1 where id = 2 and version = 1

测试MyBatis-Plus的乐观锁插件

1、数据库添加字段version

image-20230714185711215

2、实体类添加对应字段version

@Version//乐观锁注解 private Integer version;

3、注册组件

// Spring Boot 方式 @Configuration @MapperScan("按需修改") public class MybatisPlusConfig { /** * 旧版 */ @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } /** * 新版 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor; } }

4、测试

模拟成功情况

//模拟线程测试乐观锁 @Test public void testOptimisticLockerSuccess(){ //模拟成功案例 //1.查询用户信息 User user = this.userMapper.selectById(1L); //2.修改用户信息 user.setName("LYY"); //3.执行更新操作 this.userMapper.updateById(user); }

查询结果:

image-20230714185726148

修改结果:

image-20230714185735377

image-20230714185747840

模拟失败情况

@Test public void testOptimisticLockerFail(){ //模拟失败案例 //线程1 User user1 = this.userMapper.selectById(1L); user1.setAge(1); //模拟另外一个线程执行了插队操作 User user2 = this.userMapper.selectById(1L); user2.setAge(2); userMapper.updateById(user2); /** * 此时version已经和线程1启动时的1不一样了,下一步user1更新时version为2, * 和1不一样,不执行操作 */ //自旋锁来多次尝试提交! this.userMapper.updateById(user1);//如果没有乐观锁就会覆盖插队线程的值 }

查询结果:

image-20230714190155573

运行结果:

image-20230714190206512 image-20230714190229714

查询操作

查询单条

//通过id查询单条用户信息 @Test public void testSelectById(){ User user = this.userMapper.selectById(1L); System.out.println(user); }

查询结果

image-20230714190242438

查询多条 //通过多个id查询多个用户 @Test public void testSelectBatchIds(){ List users = this.userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L)); users.forEach(System.out::println); }

查询结果

image-20230714190250967

条件查询(无模糊) //条件查询 map封装(不支持模糊查询,模糊查询似乎需要条件构造器wrapper,后面再说) @Test public void testMap(){ HashMap map = new HashMap(){{ put("name", "lyy3"); put("age", 18); }}; List users = this.userMapper.selectByMap(map); users.forEach(System.out::println); }

查询结果

image-20230714190306570

分页查询

分页方式:

1. limit分页 2. pageHelper第三方插件 3. MP内置的分页插件

使用方式

1、配置分页拦截器(config添加、新版本淘汰)

//分页插件 @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }

2、使用page对象即可

page的一些方法

image-20230714190320738

//测试分页查询 public void testPage(){ //参数一current:当前页 参数二size:页面大小 Page page = new Page(3, 4); this.userMapper.selectPage(page, null); page.getRecords().forEach(System.out::println); System.out.println("总条数==>" + page.getTotal()); System.out.println("总页数==>" + page.getPages()); }

查询结果

image-20230714190355163

删除操作

基本的删除任务

image-20230714190412543

通过id删除单条 //通过id删除 @Test public void testDeleteById(){ this.userMapper.deleteById(1677522289047961606L); }

删除结果

image-20230714190456046

通过id批量删除 //通过id批量删除 @Test public void testDeletBatchIds(){ this.userMapper.deleteBatchIds(Arrays.asList(1677522289047961604L, 1677522289047961605L)); }

删除结果

![image-20230714190510071](https://img-blog.csdnimg.cn/img_convert/cc4f2ce3be172ef1115e3a7d9a2fb6d1.png

条件删除 //条件删除 @Test public void testDeleteMap(){ HashMap map = new HashMap(){{ put("age", "18"); }}; this.userMapper.deleteByMap(map); }

删除结果

image-20230714190521017

逻辑删除

物理删除:从数据库中直接删除

逻辑删除:在数据库中没有被删除,而是通过一个变量来使他失效! deleted=0 ==> deleted=1

管理员可以查看被删除的记录!防止数据的丢失,类似于回收站!

1、数据库表中增加一个deleted字段

image-20230714190531349

2、实体类中添加对应属性

@TableLogic//逻辑删除注解 private Integer deleted;

配置

//逻辑删除组件 @Bean public ISqlInjector sqlInjector(){ return new LogicSqlInjector(); } #配置逻辑删除 没删除的为0 删除的为1 mybatis-plus.global-config.db-config.logic-delete-value=1 mybatis-plus.global-config.db-config.logic-not-delete-value=0

测试逻辑删除

//测试逻辑删除 @Test public void testLogicDel(){ this.userMapper.deleteById(1677522289047961603L); }

测试结果

image-20230714190541868 image-20230714190552699

再次测试查询被删除的用户,发现查询为空

//通过id查询单条用户信息 @Test public void testSelectById(){ User user = this.userMapper.selectById(1677522289047961603L); System.out.println(user); }

image-20230714190603727

3. 性能分析插件

平时的开发中可能会遇到一些慢SQL。测试:例如druid

MyBatis-Plus也有性能分析插件,超过时间就会停止运行

性能分析拦截器作用:用于输出每条sql语句及其执行时间

1、导入插件 //性能分析插件 @Bean @Profile({"dev","test"})//设置dev开发、test测试 环境开启 保证我们的效率 public PerformanceInterceptor performanceInterceptor(){ PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); performanceInterceptor.setMaxTime(100);//设置sql最大执行时间*ms,如果超过了则不执行 performanceInterceptor.setFormat(true);//开启sql格式化 return performanceInterceptor; }

导入完后还必须要在SpringBoot中配置环境为dev(开发)或者test(测试)环境

#设置开发环境 spring.profiles.active=dev 2、测试使用 //性能测试 此时主动将最大时间设置为1ms @Test public void testTime() { //参数是一个wrapper ,条件构造器,这里我们先不用 null //查询全部的用户 List userList = userMapper.selectList(null); userList.forEach(System.out::println); }

运行结果: image-20230714190613678

将时间设置为100ms后成功: image-20230714190624544 新版性能分析插件p6spy(3.1.0 以上版本)

Maven依赖导入

p6spy p6spy 最新版本

SpringBoot配置

spring.datasource.driver-class-name: com.p6spy.engine.spy.P6SpyDriver spring.datasource.url: jdbc:p6spy:h2:mem:test ...

spy.properties 配置

#3.2.1以上使用 modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory #3.2.1以下使用或者不配置 #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定义日志打印 logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger #日志输出到控制台 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 使用日志系统记录 sql #appender=com.p6spy.engine.spy.appender.Slf4JLogger # 设置 p6spy driver 代理 deregisterdrivers=true # 取消JDBC URL前缀 useprefix=true # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. excludecategories=info,debug,result,commit,resultset # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # 实际驱动可多个 #driverlist=org.h2.Driver # 是否开启慢SQL记录 outagedetection=true # 慢SQL记录标准 2 秒 outagedetectioninterval=2 条件构造器

image-20230714190635653

测试一

@Test public void testWrapper1(){ /** * 查询name不为空,并且邮箱不为空 * 年龄大于等于12岁的用户(个人猜想ge是greater equal的缩写,大于和等于) * 可以和刚才的map对比学习 */ QueryWrapper wrapper = new QueryWrapper(); wrapper .isNotNull("name") .isNotNull("email") .ge("age", 12); this.userMapper.selectList(wrapper).forEach(System.out::println); }

运行结果

image-20230714190644516 image-20230714190654436

测试二

@Test public void testWrapper2(){ /** * 查询名字等于LYY */ QueryWrapper wrapper = new QueryWrapper(); wrapper.eq("name", "LYY"); User user = this.userMapper.selectOne(wrapper); System.out.println(user); }

运行结果

image-20230714190706650 image-20230714190714728

测试三

@Test public void testWrapper3(){ /** * 查询年龄在20~30岁之间的用户 */ QueryWrapper wrapper = new QueryWrapper(); //区间 wrapper.between("age", 20, 30); //查询结果数 Integer count = this.userMapper.selectCount(wrapper); System.out.println(count); }

运行结果

image-20230714190748592 image-20230714190756490

测试三

@Test public void testWrapper4(){ /** * 模糊查询 */ QueryWrapper wrapper = new QueryWrapper(); /** * 左和右 %e e% * 名字中不包含o * 邮箱以t开头的人 */ wrapper .notLike("name", "o") .likeRight("email", "t"); //查询结果数 List maps = this.userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }

image-20230714190804866 image-20230714190814568

测试五

@Test public void testWrapper5(){ QueryWrapper wrapper = new QueryWrapper(); /** * id在子查询中查出来 */ wrapper.inSql("id", "select id from user where id


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3